/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package TreeGenerator.Tools;

import com.jme3.bounding.BoundingBox;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.post.SceneProcessor;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
import com.jme3.util.Screenshots;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import javax.swing.JOptionPane;

/**
 *
 * @author Jannes
 */
public class RenderToTexture implements SceneProcessor{
        
    private RenderManager renderManager;    
    private FrameBuffer offBuffer;        
    private ByteBuffer cpuBuf;
    private byte[] cpuArray;
    private BufferedImage image;
    private ViewPort offView;
    private Boolean imageDone;
    private Spatial model;
    private float rotationY;
    private BoundingBox bb;
    
    public RenderToTexture(float pixelsPerUnit, RenderManager renderManager, Spatial model_, float rotationY, BoundingBox modelBound){        
        bb = modelBound;
        if(rotationY == 0 || rotationY == FastMath.PI || rotationY == FastMath.HALF_PI || rotationY == -FastMath.HALF_PI){
            RenderToTextureRotateCamera(pixelsPerUnit, renderManager, model_, rotationY, modelBound);
            //RenderToTextureRotateModel(pixelsPerUnit, renderManager, model_, rotationY);
        }else{
            JOptionPane.showMessageDialog(null,"called: RenderToTextureRotateModel");
            RenderToTextureRotateModel(pixelsPerUnit, renderManager, model_, rotationY);
        }
    }

    
    private void RenderToTextureRotateModel(float pixelsPerUnit, RenderManager renderManager, Spatial model_, float rotationY){
        /*
        model = model_.clone();
        this.renderManager = renderManager;                    
        this.rotationY = rotationY;
        imageDone = false;                
        model.rotate(0, rotationY, 0);        
        //BoundingBox bb = (BoundingBox)model.getWorldBound();      
        //Vector3f center = bb.getCenter();
        //System.out.println("center: " + bb.getCenter());        
        //float modelWidth = bb.getXExtent()+Math.abs(center.x);
        //float modelHeight = bb.getYExtent();
        Vector3f maxValues = new Vector3f();
        Vector3f minValues = new Vector3f();
        bb.getMax(maxValues);
        bb.getMin(minValues);
        float modelWidth = maxValues.x-minValues.x;
        float modelHeight = maxValues.y-minValues.y;
        int width = (int)(modelWidth*pixelsPerUnit);
        int height = (int)(modelHeight*pixelsPerUnit);
        
        cpuBuf = BufferUtils.createByteBuffer(width * height * 4);
        cpuArray = new byte[width * height * 4];
        image = new BufferedImage(width, height,BufferedImage.TYPE_4BYTE_ABGR);
        Camera offCamera = new Camera(width, height);        
        // create a pre-view. a view that is rendered before the main view        
        offView = renderManager.createPreView("Offscreen View", offCamera);        
        offView.setBackgroundColor(ColorRGBA.Blue);
        offView.setClearFlags(true, true, true);
        
        // this will let us know when the scene has been rendered to the 
        // frame buffer
        offView.addProcessor(this);

        // create offscreen framebuffer
        offBuffer = new FrameBuffer(width, height, 1);

        //setup framebuffer's cam
        //offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);        
        //offCamera.setLocation(new Vector3f(0f, 0f, -100f));
        //offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);                        
        
        //setup framebuffer's texture
//        offTex = new Texture2D(width, height, Format.RGBA8);

        //setup framebuffer to use renderbuffer
        // this is faster for gpu -> cpu copies
        offBuffer.setDepthBuffer(Format.Depth);
        offBuffer.setColorBuffer(Format.RGBA8);
//        offBuffer.setColorTexture(offTex);
        
        //set viewport to render to offscreen framebuffer
        offView.setOutputFrameBuffer(offBuffer);
       
        float frustumSize = bb.getYExtent();
        // attach the scene to the viewport to be rendered                
        offView.attachScene(model);
        
        offCamera.setLocation(model.getWorldBound().getCenter());
        offCamera.setParallelProjection(true);           
        float aspect = (float) offCamera.getWidth() / offCamera.getHeight();
        offCamera.setFrustum(-1000, 1000, -aspect*frustumSize - minValues.x, aspect * frustumSize + minValues.x, frustumSize, -frustumSize);
        */
    }
    
    private void RenderToTextureRotateCamera(float pixelsPerUnit, RenderManager renderManager, Spatial model_, float rotationY, BoundingBox modelBound){        
        model = model_.clone();
        this.renderManager = renderManager;                    
        this.rotationY = rotationY;
        imageDone = false;                
        Vector3f maxValues = new Vector3f();
        Vector3f minValues = new Vector3f();
        bb.getMax(maxValues);
        bb.getMin(minValues);
        float modelWidth = 0f;
        if(rotationY == FastMath.HALF_PI || rotationY == -FastMath.HALF_PI){
            modelWidth = maxValues.z-minValues.z;
        }else{
            modelWidth = maxValues.x-minValues.x;
        }
        float modelHeight = maxValues.y-minValues.y;
        int width = (int)(modelWidth*pixelsPerUnit);
        int height = (int)(modelHeight*pixelsPerUnit);                
        
        cpuBuf = BufferUtils.createByteBuffer(width * height * 4);
        cpuArray = new byte[width * height * 4];
        image = new BufferedImage(width, height,BufferedImage.TYPE_4BYTE_ABGR);
        Camera offCamera = new Camera(width, height);        
        // create a pre-view. a view that is rendered before the main view        
        offView = renderManager.createPreView("Offscreen View", offCamera);        
        
        offView.setBackgroundColor(ColorRGBA.Blue);
        offView.setClearFlags(true, true, true);
        
        // this will let us know when the scene has been rendered to the 
        // frame buffer
        //offView.addProcessor(this);        
        // create offscreen framebuffer
        offBuffer = new FrameBuffer(width, height, 1);

        //setup framebuffer's cam
        //offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);        
        //offCamera.setLocation(new Vector3f(0f, 0f, -100f));
        //offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);                        
        
        //setup framebuffer's texture
//        offTex = new Texture2D(width, height, Format.RGBA8);

        //setup framebuffer to use renderbuffer
        // this is faster for gpu -> cpu copies
        offBuffer.setDepthBuffer(Format.Depth);
        offBuffer.setColorBuffer(Format.RGBA8);
//        offBuffer.setColorTexture(offTex);
                
        //set viewport to render to offscreen framebuffer
        offView.setOutputFrameBuffer(offBuffer);
       
        float frustumSize = bb.getYExtent();
        // attach the scene to the viewport to be rendered 
        model.updateGeometricState();
        offView.attachScene(model);
        
        offCamera.setLocation(bb.getCenter());                       
        
        if(rotationY == FastMath.HALF_PI){            
            //offCamera.setAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X);            
            offCamera.lookAtDirection(Vector3f.UNIT_X.mult(-1), Vector3f.UNIT_Y);            
        }else if(rotationY == -FastMath.HALF_PI){
            //offCamera.setAxes(Vector3f.UNIT_Z.mult(-1), Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1));            
            offCamera.lookAtDirection(Vector3f.UNIT_X, Vector3f.UNIT_Y);            
        }else if(rotationY == 0){
            //offCamera.setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
            offCamera.lookAtDirection(Vector3f.UNIT_Z, Vector3f.UNIT_Y);            
        }else if(rotationY == FastMath.PI){
            //offCamera.setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
            offCamera.lookAtDirection(Vector3f.UNIT_Z.mult(-1), Vector3f.UNIT_Y);            
        }         
        
        offCamera.setParallelProjection(true);           
        float aspect = (float) offCamera.getWidth() / offCamera.getHeight();
        offCamera.setFrustum(-1000, 1000, -aspect * frustumSize, aspect * frustumSize, frustumSize, -frustumSize);        
        offView.addProcessor(this);        
    }
    
    public BufferedImage getImage(){
        while(!imageDone){
            try{
                Thread.sleep(10);
            }catch(Exception exc){}
        }                                
        return Transparency.makeColorTransparent(image, Color.BLUE);
    }
    
    public BufferedImage getImageBlueBackGround(){
        while(!imageDone){
            try{
                Thread.sleep(10);
            }catch(Exception exc){}
        }        
        return image;
    }
    
    public void updateImageContents(){        
        cpuBuf.clear();
        renderManager.getRenderer().readFrameBuffer(offBuffer, cpuBuf);

        synchronized (image) {
            Screenshots.convertScreenShot(cpuBuf, image);                
        }
        offView.clearScenes();
        renderManager.removePreView(offView);
        offView = null;        
        imageDone = true;        
    }
    
    
    public void initialize(RenderManager rm, ViewPort vp) {}

    public void reshape(ViewPort vp, int w, int h) {}

    public boolean isInitialized() {
        return true;
    }

    public void preFrame(float tpf) {}

    public void postQueue(RenderQueue rq) {}

    public void postFrame(FrameBuffer out) {
        updateImageContents();
    }
    public void cleanup() {}
}
